<template>
  <div class="dragContainer">
    <div class="dragContainer_left">
      <div
        id="box"
        class="dragContainer_left_main"
        :style="{
          transform: `translate(-50%, -50%) scale(${scaleMultiple})`,
          top: `${transformY}`,
          left: `${transformX}`,
        }"
        @mousedown.stop="dragDiv($event)"
        @mouseup.stop="dragUp"
        @drop.prevent="drop($event)"
        @dragover.prevent="dragover"
      >
        <!--        给元素添加鼠标按下事件不执行任何操作，调用stop修饰符阻止冒泡触发父组件事件-->
        <div
          class="box_item"
          v-for="(item, index) in boxContent"
          :key="item.id"
          @dragstart="dragstart($event, 'move', item.id)"
          draggable="true"
          @mousedown.stop="() => {}"
          @mouseenter.stop="handleItemMouseEnter(item)"
          @mouseleave.stop="handleItemMouseLeave"
          @click="showItem(item, index)"
          @dragend="dragend($event)"
          :data-type="item.type"
          :style="{
            top: `${item.positionTop - elHeight / 2}px`,
            left: `${item.positionLeft - elWidth / 2}px`,
          }"
          :class="{ box_active: item.showGroup }"
        >
          <i class="iconfont icon-light" v-if="item.type === '1'"></i>
          <i class="iconfont icon-fan" v-if="item.type === '2'"></i>
          <i class="iconfont icon-cabinet" v-if="item.type === '3'"></i>
          <span class="box_item_delete" @click.stop="deleteBoxItem(index)"
            >×</span
          >
        </div>
      </div>
    </div>
    <div class="dragContainer_right">
      <div class="dragContainer_right_operate">
        <el-button-group>
          <el-button type="primary">保 存</el-button>
          <el-button>取 消</el-button>
        </el-button-group>
      </div>
      <div class="dragContainer_right_baseTitle"><span>组件</span></div>
      <div class="dragContainer_right_appList">
        <div class="appList_item">
          <div
            class="appList_item_img"
            draggable="true"
            @dragstart="dragstart($event, 'copy')"
            @dragend="dragend($event)"
            data-type="1"
          >
            <i class="iconfont icon-light"></i>
          </div>
          <div class="appList_item_title">灯</div>
        </div>
        <div class="appList_item">
          <div
            class="appList_item_img"
            draggable="true"
            @dragstart="dragstart($event, 'copy')"
            @dragend="dragend($event)"
            data-type="2"
          >
            <i class="iconfont icon-fan"></i>
          </div>
          <div class="appList_item_title">风扇</div>
        </div>
        <div class="appList_item">
          <div
            class="appList_item_img"
            draggable="true"
            @dragstart="dragstart($event, 'copy')"
            @dragend="dragend($event)"
            data-type="3"
          >
            <i class="iconfont icon-cabinet"></i>
          </div>
          <div class="appList_item_title">控制柜</div>
        </div>
      </div>
      <div class="dragContainer_right_baseTitle"><span>属性</span></div>
      <div class="box_item_bind" v-if="bindFlag">
        <div class="box_item_bind_select">
          <el-select v-model="bindId" size="small" placeholder="Select">
            <el-option
              v-for="item in options"
              :key="item.id"
              :label="item.label"
              :value="item.id"
            />
          </el-select>
        </div>
        <div class="box_item_bind_button">
          <el-button size="small" @click="cancelBind">取消</el-button>
          <el-button size="small" type="primary" @click="doBind"
            >绑定</el-button
          >
        </div>
      </div>
    </div>
    <div class="reset">
      <el-tooltip
        class="box-item"
        effect="dark"
        content="还原"
        placement="right"
      >
        <el-icon><FullScreen @click="resetBox" /></el-icon>
      </el-tooltip>
    </div>
  </div>
</template>
<script>
export default {
  name: "dragAndDrop",
};
</script>
<script setup>
import { onMounted, onUnmounted, reactive, ref } from "vue";
//定义参数代表盒子中元素大小，这个值传入css设置元素，同时在页面回显时使用
const elWidth = ref(30);
const elHeight = ref(30);
const cssUseWidth = elWidth.value + "px";
const cssUseHeight = elHeight.value + "px";

//页面添加鼠标滚动监听，用于放大缩小展示区域
onMounted(() => {
  const el = document.getElementById("box");
  el.addEventListener("mousewheel", handleScroll);
});
onUnmounted(() => {
  const el = document.getElementById("box");
  el.removeEventListener("mousewheel", handleScroll);
});
import Big from "big.js";
const scaleMultiple = ref(1); //结合css实现放大缩小
const handleScroll = (e) => {
  if (e.deltaY < 0) {
    if (scaleMultiple.value <= 3) {
      const num = new Big(scaleMultiple.value); //使用Big.js处理数据防止js数字丢失精度
      scaleMultiple.value = Number(num.plus(0.05));
    }
  } else {
    if (scaleMultiple.value >= 0.05) {
      const num = new Big(scaleMultiple.value);
      scaleMultiple.value = Number(num.minus(0.05));
    }
  }
};
const transformX = ref("50%"); //起始偏移量，结合css保证水平居中
const transformY = ref("50%"); //起始偏移量，结合css保证垂直居中
let storeX = ref(0); //保存上次水平移动距离
let storeY = ref(0); //保存上次垂直移动距离
const dragDiv = (e) => {
  const beginX = e.clientX - storeX.value; //根据上次移动距离确定移动起始点
  const beginY = e.clientY - storeY.value;
  document.onmousemove = (e) => {
    const x = e.clientX - beginX;
    const y = e.clientY - beginY;
    storeX.value = x;
    storeY.value = y;
    transformX.value =
      x > 0 ? `calc(50% + ${Math.abs(x)}px)` : `calc(50% - ${Math.abs(x)}px)`;
    transformY.value =
      y > 0 ? `calc(50% + ${Math.abs(y)}px)` : `calc(50% - ${Math.abs(y)}px)`;
  };
};
const dragUp = () => {
  document.onmousemove = null;
  document.onmouseup = null;
};
/**
 * 拖拽元素至容器相关内容
 */
import { HashCode } from "@/utils/hash";
//生成不重复的hash字符串
const createNewId = (str) => {
  let idHasExist = boxContent.some((d) => d.id === str);
  if (idHasExist) {
    const str = HashCode(15);
    createNewId(str);
  } else {
    return str;
  }
};
const boxContent = reactive([]); //存放id=box中元素
const dragstart = (e, dragType, id = "") => {
  e.dataTransfer.setData("type", e.target.dataset.type);
  e.dataTransfer.setData("dragType", dragType);
  if (id) {
    e.dataTransfer.setData("id", id);
  }
};
const dragend = (e) => {
  e.dataTransfer.clearData();
};
const drop = (e) => {
  const dragType = e.dataTransfer.getData("dragType");
  const el = document.getElementById("box");
  const boxOffsetLeft =
    el.offsetLeft - (el.clientWidth * scaleMultiple.value) / 2; //容器距离屏幕左距离，缩放后要乘以相应的比例.除以2是源点在元素中央
  const boxOffsetTop =
    el.offsetTop - (el.clientHeight * scaleMultiple.value) / 2; //容器距离屏幕上距离，缩放后要乘以相应的比例
  const itemOffsetBoxLeft = e.clientX - boxOffsetLeft;
  const itemOffsetBoxTop = e.clientY - boxOffsetTop;
  if (dragType === "copy") {
    //复制-新增
    const id = HashCode(15);
    const item = {
      id: createNewId(id), //检查生成的id是否已经存在，确保id唯一性
      //此处若id=box元素的左边和顶部有具值元素，要相应减去具值元素对应的宽度和高度 start
      //具体算法有待到正式项目环境中调整
      positionLeft: itemOffsetBoxLeft / scaleMultiple.value,
      positionTop: itemOffsetBoxTop / scaleMultiple.value,
      //此处若id=box元素的左边和顶部有具值元素，要相应减去具值元素对应的宽度和高度 end
      type: e.dataTransfer.getData("type"),
      bindId: "", //绑定分组id
      showGroup: false, //同组元素被选中，显示相同样式
    };
    // console.log(item);
    boxContent.push(item);
  } else if (dragType === "move") {
    //移动-编辑
    //根据id查找移动元素在数组中的位置，并修改坐标信息
    const id = e.dataTransfer.getData("id");
    const index = boxContent.findIndex((d) => d.id === id);
    boxContent[index]["positionLeft"] = itemOffsetBoxLeft / scaleMultiple.value;
    boxContent[index]["positionTop"] = itemOffsetBoxTop / scaleMultiple.value;
  }
};
const dragover = () => {
  //我存在的意义是让元素能放置在id=box中，无任何执行操作
};

/**
 * 节点操作相关内容
 */
//删除元素
const deleteBoxItem = (idx) => {
  // console.log(idx)
  boxContent.splice(idx, 1);
};
//定义变量，显示/隐藏绑定下拉框
let bindFlag = ref(false);
let bindId = ref("");
let editItemIdx = ref(null);
const options = ref([
  //todo 动态生成
  {
    id: "1",
    value: "Option1",
    label: "Option1",
  },
  {
    id: "2",
    value: "Option2",
    label: "Option2",
  },
]);
//展示节点信息
const showItem = (item, idx) => {
  // console.log(item);
  bindFlag.value = true;
  bindId.value = item.bindId;
  editItemIdx.value = idx;
};
const cancelBind = () => {
  bindFlag.value = false;
  bindId.value = "";
  editItemIdx.value = null;
};
const doBind = () => {
  boxContent[editItemIdx.value].bindId = bindId.value;
  cancelBind();
  // console.log(boxContent);
};
const handleItemMouseEnter = (item) => {
  // console.log(item);
  if (item.bindId) {
    boxContent.forEach((d) => {
      if (d.bindId === item.bindId) {
        d.showGroup = true;
      }
    });
  }
};
const handleItemMouseLeave = () => {
  boxContent.forEach((d) => {
    d.showGroup = false;
  });
};
const resetBox = () => {
  scaleMultiple.value = 1;
  transformX.value = "50%";
  transformY.value = "50%";
  storeX.value = 0;
  storeY.value = 0;
};
</script>
<style scoped lang="scss">
.dragContainer {
  display: flex;
  justify-content: space-between;
  background: #f8f8f8;
  &_left {
    flex: 1;
    width: 100%;
    height: 100vh;
    position: relative;
    overflow: hidden;
    &_main {
      width: 1491px;
      height: 802px;
      background: url("../../assets/market.png") center center no-repeat;
      background-size: cover;
      position: absolute;
      //background: #fff;
      user-select: none;
      transition: transform 0.1s linear;
      .box_item {
        position: absolute;
        width: v-bind("cssUseWidth");
        height: v-bind("cssUseHeight");
        border: 1px solid transparent;
        box-sizing: border-box;
        line-height: v-bind("cssUseHeight");
        text-align: center;
        //background: #f1f1f1;
        margin-right: 10px;
        user-select: none;
        transition: border-color 0.1s linear;
        i{
          font-size: 26px;
        }
        &:hover {
          .box_item_delete {
            display: block;
          }
        }
        &_delete {
          width: 15px;
          font-size: 12px;
          height: 15px;
          line-height: 12px;
          background: #e60012;
          color: #fff;
          text-align: center;
          position: absolute;
          top: -6px;
          right: -6px;
          border-radius: 50%;
          cursor: pointer;
          display: none;
        }
      }
      .box_active {
        border-color: #e60012;
      }
    }
  }
  &_right {
    flex: 0 0 200px;
    width: 200px;
    height: 100vh;
    overflow-y: auto;
    background: #ffffff;
    box-shadow: -1px 0 5px 1px #ddd;
    &_operate {
      padding: 10px;
      box-sizing: border-box;
      width: 100%;
      ::v-deep(.el-button-group) {
        width: 100%;
      }
      ::v-deep(.el-button) {
        width: 50%;
      }
    }
    &_baseTitle {
      font-size: 12px;
      padding: 3px 8px;
      color: #666;
      background-color: #efefef;
      border-radius: 3px;
      margin-top: 8px;
      margin-bottom: 4px;
    }
    &_appList {
      padding: 10px;
      box-sizing: border-box;
      display: flex;
      //min-height: 400px;
      flex-wrap: wrap;
      .appList_item {
        text-align: center;
        margin-right: 20px;
        user-select: none;
        &_img {
          margin-bottom: 10px;
          i {
            color: #000;
          }
        }
        &_title {
          font-size: 12px;
          color: #666;
          transform: scale(0.7);
          white-space: nowrap;
          line-height: 0.7;
        }
      }
    }
    .box_item_bind {
      padding: 10px;
      box-sizing: border-box;
      &_select {
        margin-bottom: 10px;
      }
    }
  }
  .reset {
    position: fixed;
    bottom: 30px;
    left: 30px;
    cursor: pointer;
    font-size: 25px;
    z-index: 1000;
  }
}
</style>
